package com.leon.rose.modules.upms.shiro;

import com.leon.rose.modules.upms.entity.SysResources;
import com.leon.rose.modules.upms.entity.SysRole;
import com.leon.rose.modules.upms.entity.SysUser;
import com.leon.rose.modules.upms.service.ISysResourcesService;
import com.leon.rose.modules.upms.service.ISysRoleService;
import com.leon.rose.modules.upms.service.ISysUserService;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

/*
shiro系列参考：https://blog.csdn.net/lishehe/article/details/45219023
shiro认证流程
1应用程序根据用户的身份和凭证（principals和credentials）来构造出AuthenticationToken实例，并调用Subject.login的方法进行登录，其会自动委托给Security
Manager。
2Subject实例通常上都是DelegatingSubject
(或子类)，在验证开始的时候，Subject实例会将验证委托给应用程序配置的SecurityManager，并调用securityManager.login(token)方法进行身份认证。
3SecurityManager得到token信息后，通过调用authenticator.authenticate(token)方法，把身份验证委托给内置的Authenticator的实例进行验证。authenticator通常是ModularRealmAuthenticator
实例，支持对一个或多个Realm实例进行适配。ModularRealmAuthenticator提供了一种可插拔的认证风格，你可以在此处插入自定义Realm实现。
4如果配置了多个Realm，ModularRealmAuthenticator会根据配置的AuthenticationStrategy（身份验证策略）进行多Realm认证过程
注：如果应用程序中仅配置了一个Realm，Realm将被直接调用而无需再配置认证策略。
5判断每个Realm是否支持提交的token，如果支持Realm就会调用getAuthenticationInfo（token）方法进行认证处理。
*/

@AllArgsConstructor
@Component
public class UserRealm extends AuthorizingRealm {

    public static final Integer LOCK=0;

    public static final Integer SUPER_ADMIN=1;

    private ISysUserService userService;

    private ISysResourcesService resourcesService;

    private ISysRoleService roleService;


    /**
     * @Description:登陆验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SysUser user = userService.getUserByUsername(username);//用token得到的用户名查询数据库得到用户信息
        if (user == null) {
            throw new UnknownAccountException("账号不存在！");//没找到帐号
        }
        if (user.getStatus()!=null&&user.getStatus().equals(LOCK)) {
            throw new LockedAccountException("帐号已被锁定，禁止登录！"); //帐号锁定
        }
        // principal参数使用用户Id，方便动态刷新用户权限
        return new SimpleAuthenticationInfo(
            user.getId(),
            user.getPassword(),
            ByteSource.Util.bytes(username),
            getName()
        );
    }


    /**
     * @Description:授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Integer userId = (Integer) SecurityUtils.getSubject().getPrincipal();

        // 赋予角色
        List<SysRole> roleList = roleService.listByUserId(userId);
        for (SysRole role : roleList) {
            info.addRole(role.getName());
        }

        // 赋予权限
        List<SysResources> resourcesList = null;
        SysUser user = userService.getById(userId);
        if (null == user) {
            return info;
        }
        // ROOT用户默认拥有所有权限
        if (user.getId().equals(SUPER_ADMIN)) {
            resourcesList = resourcesService.list();
        } else {
            resourcesList = resourcesService.listByUserId(userId);
        }

        if (!CollectionUtils.isEmpty(resourcesList)) {
            Set<String> permissionSet = new HashSet<>();
            for (SysResources resources : resourcesList) {
                String permission = resources.getPermission();
                if (StringUtils.isNotBlank(permission)) {
                    permissionSet.addAll(Arrays.asList(permission.trim().split(",")));
                }
            }
            info.setStringPermissions(permissionSet);
        }
        return info;
    }

}
